package com.sunchangming.stmdb.xgen;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.log4j.BasicConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.thoughtworks.xstream.XStream;

import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;

public final class Main {
	private final static Logger logger = LoggerFactory.getLogger(Main.class);

	private static final String[] javaKeywords = { "abstract", "continue", "for", "new", "switch", "assert", "default",
			"goto", "package", "synchronized", "boolean", "do", "if", "private", "this", "break", "double",
			"implements", "protected", "throw", "byte", "else", "import", "public", "throws", "case", "enum",
			"instanceof", "return", "transient", "catch", "extends", "int", "short", "try", "char", "final",
			"interface", "static", "void", "class", "finally", "long", "strictfp", "volatile", "const", "float",
			"native", "super", "while" };

	static boolean isValidForName(String s) {
		for (final String n : javaKeywords) {
			if (n.equals(s))
				return false;
		}
		return true;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// 配置Log4j
		if (Files.exists(Paths.get("log4j.xml")))
			org.apache.log4j.xml.DOMConfigurator.configure("log4j.xml");
		else
			BasicConfigurator.configure();

		// 分析命令行参数
		final Options options = new Options();
		options.addOption("o", true, "output dir");
		options.addOption("h", false, "help");
		options.addOption("p", false, "proto dir");

		final HelpFormatter formatter = new HelpFormatter();

		final CommandLineParser parser = new PosixParser();
		final CommandLine cmd;
		try {
			cmd = parser.parse(options, args);
		} catch (final ParseException ex) {
			formatter.printHelp("com.sunchangming.stmdb.xgen.Main", options);
			return;
		}

		if (cmd.hasOption("h") || cmd.getArgs().length != 1) {
			formatter.printHelp("com.sunchangming.stmdb.xgen.Main", options);
			return;
		}

		String srcdir = "src";
		if (cmd.hasOption('o')) {
			srcdir = cmd.getOptionValue('o');
		}

		String protodir = "";
		if (cmd.hasOption('p')) {
			protodir = cmd.getOptionValue('p');
		}

		final String language = "java";
		final java.nio.file.Path src = java.nio.file.Paths.get(srcdir);
		final java.nio.file.Path configxml = java.nio.file.Paths.get(cmd.getArgs()[0]);

		// 执行工作
		if (doWork(src, configxml, protodir.isEmpty() ? null : java.nio.file.Paths.get(protodir), language) != 0)
			throw new RuntimeException("代码生成失败");
	}

	private static Configuration fmcfg = null;

	/**
	 * 得到FreeMarker的 Configuration 的一个单件。
	 * 
	 * @return freemarker的Configuration
	 */
	static synchronized Configuration getFreeMarkerConfig() {
		if (fmcfg != null)
			return fmcfg;
		fmcfg = new Configuration();

		try {
			fmcfg.setDirectoryForTemplateLoading(new java.io.File("tmpl"));
		} catch (final IOException ex) {
			logger.error("读取模板文件失败");
			return null;
		}
		fmcfg.setDefaultEncoding("utf-8");

		fmcfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
		return fmcfg;
	}

	/**
	 * 
	 * @param src
	 *            生成的源代码输出到哪个目录
	 * @param configxml
	 *            定义messages/beans的那个配置文件
	 * @throws Exception
	 */
	static int doWork(final java.nio.file.Path src, final java.nio.file.Path configxml,
			final java.nio.file.Path protodir, String language) throws Exception {
		// 初始化Types单件。
		Types.init();
		// 检查配置文件是否存在
		if (!java.nio.file.Files.exists(configxml)) {
			logger.error("configxml doesn't exists," + configxml.toAbsolutePath().toString());
			return -1;
		}

		final StmdbConfig ns;
		final XStream xstream = StmdbConfig.getXStream();
		final java.io.InputStream is = java.nio.file.Files.newInputStream(configxml);
		try {
			final Object obj = xstream.fromXML(is);
			if (obj instanceof StmdbConfig) {
				ns = (StmdbConfig) obj;
			} else {
				logger.error("错误的xml");
				return -1;
			}
		} finally {
			is.close();
		}

		try {
			ns.types();
			ns.compile();
			ns.generateCode(src, language);
		} catch (final ConfigError ex) {
			logger.error("配置错误：" + ex.getMessage());
			return -1;
		}

		if (protodir != null) {
			if (!java.nio.file.Files.exists(protodir))
				java.nio.file.Files.createDirectories(protodir);
			if (!java.nio.file.Files.exists(protodir)) {
				logger.error("output dir doesn't exists");
				return -1;
			}

			final ProcessBuilder pb = new ProcessBuilder("protoc", "--proto_path="
					+ protodir.toAbsolutePath().toString(), "--java_out=" + src.toAbsolutePath().toString(), protodir
					.toAbsolutePath().toString() + "/*.proto");
			final Process protocProcess = pb.start();
			protocProcess.waitFor();
		}
		return 0;
	}
}
